<?php

/**
 * Created By PhpStorm
 * User sclecon
 * Contact Email 27941662@qq.com
 * Time 2021/8/27 16:30
 */


namespace wowo;


class Container
{
    private $Containers = [];
    private $reflectionClass = [];
    private static $instance = null;
    private function __construct(){}
    private function __clone(){}

    public static function getInstance(){
        if (is_null(self::$instance)){
            self::$instance = new self();
        }
        return self::$instance;
    }

    public static function get(string $name){
        return self::getInstance()->make($name);
    }

    public function make(string $name, array $vers = []){
        try {
            if (array_key_exists($name, $this->Containers) === false){
                $reflectionClass = $this->getReflectionClass($name);
                $constructor = $reflectionClass->getConstructor();
                $params = [];
                if (!is_null($constructor)){
                    $constructorParams = $constructor->getParameters();
                    foreach ($constructorParams as $constructorParam) {
                        if (!is_null($constructorParam->getClass())){
                            $params[] = $this->make($constructorParam->getClass()->name);
                        }else{
                            $params[] = $constructorParam->name;
                        }
                    }
                }
                $this->Containers[$name] = $reflectionClass->newInstanceArgs($params);
            }
            return $this->Containers[$name];
        } catch (\ReflectionException $e){
            exit('Container Error: '.$e->getMessage());
        }
    }

    public function invokeArgs(string $class, string $method, array $args = []){
        $reflectionClass = $this->getReflectionClass($class);
        if ($reflectionClass->hasMethod($method)){
            $method = $reflectionClass->getMethod($method);
            $args = $this->bindParams($method, $args);
        }
        return $args;
    }

    protected function bindParams(\ReflectionFunctionAbstract $method, array $vars = []){
        // 获取需要输入的参数个数
        if ($method->getNumberOfParameters() === 0){
            return [];  // 需要传入的参数为0
        }
        reset($vars);
        $type = key($vars) === 0 ? 1 : 0;
        $params = $method->getParameters();
        $args = [];
        foreach ($params as $param){
            $name = $param->getName();
            $class = $param->getClass();
            if ($class){
                $args[] = $this->make($class->getName());
            } elseif (1 == $type && !empty($vars)) {
                $args[] = array_shift($vars);
            } elseif (0 == $type && array_key_exists($name, $vars)) {
                $args[] = $vars[$name];
            } elseif ($param->isDefaultValueAvailable()) {
                $args[] = $param->getDefaultValue();
            }
        }
        return $args;
    }

    protected function getReflectionClass(string $class): \ReflectionClass
    {
        if (!array_key_exists($class, $this->reflectionClass)){
            try {
                $this->reflectionClass[$class] = new \ReflectionClass($class);
            } catch (\ReflectionException $e) {
                exit('Container Error: '.$e->getMessage());
            }
        }
        return $this->reflectionClass[$class];
    }
}